/*
 * AFND.cpp
 */

#include <iostream>
#include <sstream>
#include "AFND.h"

using namespace std;

AFND::AFND(set<char> &alfabeto) {
    this->_alfabeto = alfabeto;

    this->_inicial = agregarEstado();
    Estado final = agregarEstado();

    this->_finales.insert(final);

    set<char>::iterator it;
    for (it = this->_alfabeto.begin(); it != this->_alfabeto.end(); it++){
        agregarTransicion(this->_inicial, *it, final);
    }
}

AFND::AFND(char car, set<char> &alfabeto) {
    this->_alfabeto = alfabeto;

    this->_inicial = agregarEstado();
    Estado final = agregarEstado();

    this->_finales.insert(final);

    agregarTransicion(this->_inicial, car, final);
}

AFND::~AFND() {
    // TODO Auto-generated destructor stub
}

/* Se podria mejorar.. */
set<int> AFND::recorrerEstados(set<int> estados, char c) const
{
    set<int> ant, sig;
    set<int>::iterator it;

    /* Avanzamos los estados con el caracter pedido */
    if (c != AFND::LAMBDA) {
        for (it = estados.begin(); it != estados.end(); it++) {
            const set<int> next = sigma(*it, c);
            sig.insert(next.begin(), next.end());
        }
    } else {
        sig = estados;
    }

    /* Luego progresamos con lambda hasta donde se pueda */
    do {
        ant = sig;
        for (it = ant.begin(); it != ant.end(); it++) {
            const set<int> next = sigma(*it, AFND::LAMBDA);
            sig.insert(next.begin(), next.end());
        }
    } while (sig != ant);

    return sig;
}


int AFND::inicial() const {
    return this->_inicial;
}

set<char> AFND::alfabeto() const {
    return this->_alfabeto;
}

set<int> AFND::estados() const {
    return this->_estados;
}

set<int> AFND::finales() const {
    return this->_finales;
}

const set<int> AFND::sigma(int e, char c) const {
    AFND::EstadoCaracter ec(e, c);
    mapaTrans::const_iterator it = this->_transiciones.find(ec);
    if (it != _transiciones.end()) {
        return it->second;
    } else {
        set<int> tmp;
        return tmp;
    }
}

AFND* AFND::concatenar(AFND& afnd){
    int offset = this->_ultimo;

    // renombre y union de estados
    set<AFND::Estado>::iterator itQ;
    for (itQ = afnd._estados.begin(); itQ != afnd._estados.end(); itQ++){
        Estado viejo = *itQ;
        Estado nuevo = viejo + offset;
        this->_estados.insert(nuevo);
    }

    // aplicar renombre a estados y unir transiciones
    AFND::mapaTrans::iterator itS;
    for (itS = afnd._transiciones.begin(); itS != afnd._transiciones.end();
                                                                itS++) {
        AFND::EstadoCaracter eyc = itS->first;
        set<AFND::Estado> destinos = itS->second;

        eyc._estado += offset;

        set<AFND::Estado>::iterator itQ;
        for (itQ = destinos.begin(); itQ != destinos.end(); itQ++) {
            this->_transiciones[eyc].insert((*itQ) + offset);
        }
    }

    Estado nuevoInicial = afnd._inicial + offset;

    // Transicion lambda desde viejos finales a nuevo inicial
    set<AFND::Estado>::iterator itF;
    for (itF = this->_finales.begin(); itF != this->_finales.end(); itF++) {
        agregarTransicion(*itF, LAMBDA, nuevoInicial);
    }

    // Elminar viejos finales
    this->_finales.clear();

    // agregar nuevos finales renombrados
    set<AFND::Estado>::iterator itNF;
    for (itNF = afnd._finales.begin(); itNF != afnd._finales.end(); itNF++) {
        this->_finales.insert((*itNF) + offset);
    }

    //Actualizo maximo valor (se que el set esta ordenado)
    this->_ultimo = *(this->_estados.rbegin());

    return this;
}

AFND* AFND::alternativa(AFND& afnd){
    int offset = this->_ultimo;

    Estado viejoInicial1 = this->_inicial;
    Estado viejoInicial2 = afnd._inicial + offset;

    // renombre y union de estados
    set<AFND::Estado>::iterator itQ;
    for (itQ = afnd._estados.begin(); itQ != afnd._estados.end(); itQ++) {
        Estado viejo = *itQ;
        Estado nuevo = viejo + offset;
        this->_estados.insert(nuevo);
    }

    // aplicar renombre a estados y unir transiciones
    AFND::mapaTrans::iterator itS;
    for (itS = afnd._transiciones.begin(); itS != afnd._transiciones.end();
                                                                    itS++) {
        AFND::EstadoCaracter eyc = itS->first;
        set<AFND::Estado> destinos = itS->second;

        eyc._estado += offset;

        set<AFND::Estado>::iterator itQ;
        for (itQ = destinos.begin(); itQ!=destinos.end(); itQ++) {
            this->_transiciones[eyc].insert((*itQ) + offset);
        }
    }

    //Actualizo maximo valor (se que el set esta ordenado)
    this->_ultimo = *(this->_estados.rbegin());

    //crear nuevo inicial y crear transicioes lambda a viejos iniciales
    Estado nuevoInicial = agregarEstado();

    this->_inicial = nuevoInicial;

    agregarTransicion(nuevoInicial, LAMBDA, viejoInicial1);
    agregarTransicion(nuevoInicial, LAMBDA, viejoInicial2);

    // Crear nuevo final y crear transiciones lambda desde viejos finales
    // a nuevo final
    Estado nuevoFinal = agregarEstado();
    set<AFND::Estado>::iterator itF;

    for (itF = this->_finales.begin(); itF != this->_finales.end(); itF++) {
        agregarTransicion(*itF, LAMBDA, nuevoFinal);
    }

    for (itF = afnd._finales.begin(); itF != afnd._finales.end(); itF++) {
        agregarTransicion(*itF + offset, LAMBDA, nuevoFinal);
    }

    // Elminar viejos finales
    this->_finales.clear();

    // Marcar nuevo final como final
    this->_finales.insert(nuevoFinal);

    return this;
}

void AFND::hacerOpcional(){
    Estado nuevoInicial = agregarEstado();
    agregarTransicion(nuevoInicial, LAMBDA, _inicial);
    _inicial = nuevoInicial;
    _finales.insert(_inicial);
}

void AFND::clausurar(){
    // a* = a+?
    clausurarPositivamente();
    hacerOpcional();
}

void AFND::clausurarPositivamente(){
    // crear transicion lambda desde finales al inicial
    set<AFND::Estado>::iterator itF;
    for (itF = this->_finales.begin(); itF != this->_finales.end(); itF++) {
        agregarTransicion(*itF, LAMBDA, this->_inicial);
    }
}

AFND::Estado AFND::agregarEstado(){
    Estado nuevo = ++(_ultimo);
    _estados.insert(nuevo);
    return nuevo;
}

void AFND::agregarTransicion(Estado origen, char caracter, Estado destino) {
    EstadoCaracter eyc(origen, caracter);
    this->_transiciones[eyc].insert(destino);
}

ostream& operator<< (ostream &out, AFND &afnd){

    out << "A = <Q, A, s, q, F>" << endl;

    out << "Q = {";
    set<AFND::Estado>::iterator itQ;
    for (itQ = afnd._estados.begin(); itQ != afnd._estados.end(); itQ++) {
        out << *itQ << ", ";
    }
    out << "}" << endl;

    out << "A = {";
    set<char>::iterator itA;
    for (itA = afnd._alfabeto.begin(); itA != afnd._alfabeto.end(); itA++) {
        out << *itA << ", ";
    }
    out << "}" << endl;

    out << "s = {" << endl;
    AFND::mapaTrans::iterator itS;
    for (itS = afnd._transiciones.begin(); itS != afnd._transiciones.end();
                                                                    itS++) {

        AFND::EstadoCaracter eyc = itS->first;
        set<AFND::Estado> destinos = itS->second;

        out << "\t" <<  eyc._estado << " x " << eyc._caracter << " -> {";

        set<AFND::Estado>::iterator itQ;
        for (itQ = destinos.begin(); itQ != destinos.end(); itQ++){
            out << *itQ << ", ";
        }
        out << "}"<< endl;
    }
    out << "}" << endl;

    out << "q = " << afnd._inicial << endl;

    out << "F = {";
    set<AFND::Estado>::iterator itF;
    for (itF = afnd._finales.begin(); itF != afnd._finales.end(); itF++) {
        out << *itF << ", ";
    }
    out << "}" << endl;

    return out;
}
